package com.xiaomaoguai.mybatisplus.generator;

import com.xiaomaoguai.mybatisplus.exception.MybatisPlusException;
import com.xiaomaoguai.mybatisplus.generator.core.ColumnMeta;
import com.xiaomaoguai.mybatisplus.generator.core.TableMeta;
import com.xiaomaoguai.mybatisplus.generator.core.TemplateType;
import com.xiaomaoguai.mybatisplus.generator.core.TypeMapping;
import com.xiaomaoguai.mybatisplus.utils.FreemarkerTemplateHelper;
import com.xiaomaoguai.mybatisplus.utils.StrKit;
import com.xiaomaoguai.mybatisplus.utils.SystemConfig;
import freemarker.template.TemplateException;
import org.aeonbits.owner.ConfigFactory;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @fileName: AutoGenerator.java
 * @author: WeiHui.Zhang
 * @date: 2016-09-22  15:11
 * @version: v1.0.0
 */
public class AutoGenerator {

	protected GeneratorConfig generatorConfig;

	protected SystemConfig systemConfig;

	protected String PATH_ENTITY = null;
	protected String PATH_MAPPER = null;
	protected String PATH_MAPPER_XML = null;
	protected String PATH_SERVICE = null;
	protected String PATH_SERVICE_IMPL = null;
	protected String PATH_SERVER = null;
	protected String PATH_SERVER_IMPL = null;
	protected String PATH_CONTROLLER = null;
	protected String PATH_JSP = null;

	protected static boolean FILE_OVERRIDE = false;

	protected static final String JAVA_SUFFIX = ".java";
	protected static final String XML_SUFFIX = ".xml";


	public AutoGenerator(GeneratorConfig config) {
		this.generatorConfig = config;
		this.systemConfig = ConfigFactory.create(SystemConfig.class);
		setUp();
	}

	private void setUp() {
		File gf = new File(systemConfig.saveDir());
		if (!gf.exists()) {
			gf.mkdirs();
		}
		/**
		 * 修改设置最终目录的逻辑
		 */
		String saveDir = gf.getPath();
		PATH_ENTITY = getFilePath(saveDir, getPathFromPackageName(systemConfig.entityPackage()));
		PATH_MAPPER = getFilePath(saveDir, getPathFromPackageName(systemConfig.mapperPackage()));
		PATH_MAPPER_XML = getFilePath(saveDir, getPathFromPackageName(systemConfig.mapperXmlPackage()));
		PATH_SERVICE = getFilePath(saveDir, getPathFromPackageName(systemConfig.servicePackage()));
		PATH_SERVICE_IMPL = getFilePath(saveDir, getPathFromPackageName(systemConfig.serverImplPackage()));
		PATH_SERVER = getFilePath(saveDir, getPathFromPackageName(systemConfig.serverPackage()));
		PATH_SERVER_IMPL = getFilePath(saveDir, getPathFromPackageName(systemConfig.serverImplPackage()));
		PATH_CONTROLLER = getFilePath(saveDir, getPathFromPackageName(systemConfig.controllerPackage()));
		PATH_JSP = getFilePath(saveDir, "WEB-INF/views/");
	}

	public void run() {
		generate();
		autoOpen();
		System.out.println(" generate success! ");
	}

	private void autoOpen() {
		try {
			String osName = System.getProperty("os.name");
			if (osName != null) {
				if (osName.contains("Mac")) {
					Runtime.getRuntime().exec("open " + systemConfig.saveDir());
				} else if (osName.contains("Windows")) {
					Runtime.getRuntime().exec("cmd /c start " + systemConfig.saveDir());
				} else {
					System.err.println("save dir:" + systemConfig.saveDir());
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 生成文件地址
	 *
	 * @param segment 文件地址片段
	 * @return
	 */
	protected static String getFilePath(String savePath, String segment) {
		File folder = new File(savePath + File.separator + segment);
		if (!folder.exists()) {
			folder.mkdirs();
		}
		return folder.getPath();
	}

	/**
	 * 根据包名转换成具体路径
	 *
	 * @param packageName
	 * @return
	 */
	protected static String getPathFromPackageName(String packageName) {
		if (StringUtils.isEmpty(packageName)) {
			return "";
		}
		return packageName.replace(".", File.separator);
	}

	/**
	 * 生成映射文件
	 */
	public void generate() {
		Connection conn = null;
		try {
			/**
			 * 创建连接
			 */
			Class.forName(generatorConfig.getDbDriverName());
			conn = DriverManager.getConnection(generatorConfig.getDbUrl(), generatorConfig.getDbUser(), generatorConfig.getDbPassword());

			/**
			 * 根据配置获取应该生成文件的表信息
			 */
			List<String> tables = getTables(conn);
			if (null == tables) {
				return;
			}
			Map<String, String> tableComments = getTableComment(conn);

			//准备Table元数据
			List<TableMeta> tableMetas = new ArrayList<>();

			for (String table : tables) {
				TableMeta tableMeta = new TableMeta();
				tableMeta.setName(table);
				tableMeta.setModelName(buildModelName(table));
				tableMeta.setRemarks(tableComments.get(table));

				String tableFieldsSql = String.format(config.getConfigDataSource().getTableFieldsSql(), table);
				ResultSet results = conn.prepareStatement(tableFieldsSql).executeQuery();
				while (results.next()) {
					ColumnMeta columnMeta = new ColumnMeta();
					columnMeta.setName(results.getString(config.getConfigDataSource().getFieldName()));
					columnMeta.setAttrName(StrKit.toCamelCase(columnMeta.getName().toLowerCase()));
					columnMeta.setJavaType(TypeMapping.getType(config.getConfigDataSource(),
							results.getString(config.getConfigDataSource().getFieldType())));
					columnMeta.setComment(results.getString(config.getConfigDataSource().getFieldComment()));
					tableMeta.getColumnMetas().add(columnMeta);
				}
				tableMetas.add(tableMeta);
			}

			//生成
			for (TableMeta tableMeta : tableMetas) {
				generateFiles(tableMeta);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (conn != null) {
				try {
					conn.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}
	}

	/**
	 * 生成文件
	 *
	 * @param tableMeta
	 */
	private void generateFiles(TableMeta tableMeta) {
		generateCode(tableMeta, TemplateType.Controller, PATH_CONTROLLER);
		generateCode(tableMeta, TemplateType.Server, PATH_SERVER);
		generateCode(tableMeta, TemplateType.ServerImpl, PATH_SERVER_IMPL);
		generateCode(tableMeta, TemplateType.Service, PATH_SERVICE);
		generateCode(tableMeta, TemplateType.ServiceImpl, PATH_SERVICE_IMPL);
		generateCode(tableMeta, TemplateType.Mapper, PATH_MAPPER);
		generateCode(tableMeta, TemplateType.MapperXml, PATH_MAPPER_XML);
	}

	/**
	 * 生成代码文件
	 *
	 * @param tableMeta    要生成的表名
	 * @param templateType 模版枚举，详情参考类上面的TemplateType枚举
	 */
	private void generateCode(TableMeta tableMeta, TemplateType templateType, String filePath) {
		String path = SystemConfig.TEMPLATE_PATH;
		try {
			/**
			 * 输入模版需要参数
			 */
			Map<String, Object> root = new HashMap<>();
			root.put("createdName", systemConfig.createdName());
			root.put("createdTime", SystemConfig.createdTime);
			root.put("tableMeta", tableMeta);
			/**
			 * 生成的文件路径,如:
			 */
			StringBuilder sb = new StringBuilder(filePath);
			if (templateType.name().equals(TemplateType.Controller.name())) {
				sb.append(tableMeta.getModelName() + "Controller");
			}
			if (templateType.name().equals(TemplateType.Server.name())) {
				sb.append(tableMeta.getModelName() + "Server");
			}
			if (templateType.name().equals(TemplateType.ServerImpl.name())) {
				sb.append(tableMeta.getModelName() + "ServerImpl");
			}
			if (templateType.name().equals(TemplateType.Service.name())) {
				sb.append(tableMeta.getModelName() + "ServerImpl");
			}
			if (templateType.name().equals(TemplateType.ServiceImpl.name())) {
				sb.append(tableMeta.getModelName() + "ServerImpl");
			}
			if (templateType.name().equals(TemplateType.Mapper.name())) {
				sb.append(tableMeta.getModelName() + "Mapper");
			}
			if (templateType.name().equals(TemplateType.MapperXml.name())) {
				sb.append(tableMeta.getModelName() + "Mapper");
			}
			sb.append(templateType.name().equals(TemplateType.MapperXml.name()) ? XML_SUFFIX : JAVA_SUFFIX);
			/**
			 * 调用Freemarker模版引擎 传入模版名称 和参数 输出指定文件
			 */
			FreemarkerTemplateHelper.loadTemplateByFile(new File(path), templateType.getCode(), "UTF-8", root,
					sb.toString());
		} catch (IOException e) {
			e.printStackTrace();
		} catch (TemplateException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 检查配置的所有表是否在dbTables里存在.
	 * <ul>
	 * <li>如果未配置，则返回所有表</li>
	 * <li>所有都存在直接返回custTables.</li>
	 * <li>如果发现有不存在表名直接返回null。</li>
	 * </ul>
	 *
	 * @return
	 * @throws SQLException
	 */
	protected List<String> getTables(Connection conn) throws SQLException {
		List<String> result = new ArrayList<>();

		List<String> tables = new ArrayList<>();
		PreparedStatement statement = conn.prepareStatement(config.getConfigDataSource().getTablesSql());
		ResultSet results = statement.executeQuery();
		while (results.next()) {
			tables.add(results.getString(1));
		}
		//过滤排除的表
		Set<String> excludedTables = config.getExcludedTables();
		Collection<String> leftTables = CollectionUtils.removeAll(tables, excludedTables);
		//过滤包含的表
		Set<String> includedTables = config.getIncludedTables();
		if (includedTables.isEmpty()) {
			result.addAll(leftTables);
		} else {
			// 循环判断是否配置的所有表都在当前库中存在
			List<String> notExistTables = new ArrayList<>();
			for (String tb : includedTables) {
				if (!leftTables.contains(tb)) {
					notExistTables.add(tb);
				}
			}
			if (notExistTables.isEmpty()) {
				result.addAll(includedTables);
			} else {
				// 如果有错误的表名，打印到控制台，且返回null
				System.err.println("tablename " + notExistTables.toString() + " is not exist!!");
			}
		}
		return result.isEmpty() ? null : result;
	}

	/**
	 * 获取所有的数据库表注释
	 *
	 * @return
	 * @throws SQLException
	 */
	protected Map<String, String> getTableComment(Connection conn) throws SQLException {
		Map<String, String> maps = new HashMap<String, String>();
		PreparedStatement pstate = conn.prepareStatement(generatorConfig.getConfigDataSource().getTableCommentsSql());
		ResultSet results = pstate.executeQuery();
		while (results.next()) {
			maps.put(results.getString(generatorConfig.getConfigDataSource().getTableName()),
					results.getString(generatorConfig.getConfigDataSource().getTableComment()));
		}
		return maps;
	}

	/**
	 * 生成 beanName
	 *
	 * @param tableName 表名
	 * @return beanName
	 */
	protected String buildModelName(String tableName) {
		// 移除表名前缀仅用于生成 modelName、baseModelName，而 tableMeta.name 表名自身不能受影响
		String[] tablePrefix = this.generatorConfig.getTablePrefix();
		if (tablePrefix != null) {
			//对 tablePrefix 排序， 去掉越长的前缀越在前
			List<String> strings = Arrays.asList(tablePrefix);
			Collections.sort(strings, new Comparator<String>() {
				@Override
				public int compare(String o1, String o2) {
					return o1.length() - o2.length() >= 0 ? -1 : 1;
				}
			});
			for (String prefix : strings) {
				if (tableName.startsWith(prefix)) {
					tableName = tableName.replaceFirst(prefix, "");
					break;
				}
			}
		}
		return StrKit.firstCharToUpperCase(StrKit.toCamelCase(tableName));
	}

}
